#include "CpuZ80.h"
#include "CpuZ80Macros.h"
#include <cassert>

byte_t CpuZ80::HandleOpcode_FD(byte_t opcode)
{
	switch(opcode)
	{

		// Forward to double prefixed handler
	case 0xCB: 
	{
		// First the operand, then the opcode
		byte_t operand = FetchByte();
		byte_t opcode = FetchByte();
		return HandleOpcode_FDCB(operand, opcode);
	}																	break;

	case 0x09: RegIY = AddWord(RegIY, RegBC);							break;	// ADD IY,BC  
	case 0x19: RegIY = AddWord(RegIY, RegDE);							break;	// ADD IY,DE   
	case 0x29: RegIY = AddWord(RegIY, RegIY);							break;	// ADD IY,IY
	case 0x39: RegIY = AddWord(RegIY, RegSP);							break;	// ADD IY,SP

	case 0x21: RegIY = FetchWord();										break;	// LD IY,NN
	case 0x2A: RegIY = ReadWord(FetchWord());							break;	// LD IY,(NN) 
	case 0x22: WriteWord(RegIY, FetchWord());							break;	// LD (NN),IY

	case 0x23: RegIY++;													break;	// INC IY
	case 0x2B: RegIY--;													break;	// DEC IY

	case 0x24: RegIYH = IncrementByte(RegIYH);							break; // INC IYH
	case 0x25: RegIYH = DecrementByte(RegIYH);							break; // DEC IYH

	case 0x2C: RegIYL = IncrementByte(RegIYL);							break; // INC IYL
	case 0x2D: RegIYL = DecrementByte(RegIYL);							break; // DEC IYL

	case 0x34:
	{
		word_t address = AddDisplacement(RegIY, FetchByte());
		WriteByte(IncrementByte(ReadByte(address)), address);
	}																	break;	// INC (IY+N)
	case 0x35:
	{
		word_t address = AddDisplacement(RegIY, FetchByte());
		WriteByte(DecrementByte(ReadByte(address)), address);
	}																	break;	// DEC (IY+N)
	case 0x36:
	{
		word_t address = AddDisplacement(RegIY, FetchByte());
		WriteByte(FetchByte(), address);
	}																	break; 	// LD (IY+N),N

	case 0x46: RegB = ReadByte(AddDisplacement(RegIY, FetchByte()));	break;	// LD B, (IY+N)
	case 0x4E: RegC = ReadByte(AddDisplacement(RegIY, FetchByte()));	break;	// LD C, (IY+N)
	case 0x56: RegD = ReadByte(AddDisplacement(RegIY, FetchByte()));	break;	// LD D, (IY+N)
	case 0x5E: RegE = ReadByte(AddDisplacement(RegIY, FetchByte()));	break;	// LD E, (IY+N)
	case 0x66: RegH = ReadByte(AddDisplacement(RegIY, FetchByte()));	break;	// LD H, (IY+N)
	case 0x6E: RegL = ReadByte(AddDisplacement(RegIY, FetchByte()));	break;	// LD L, (IY+N)
	case 0x7E: RegA = ReadByte(AddDisplacement(RegIY, FetchByte()));	break;	// LD A,(IY+N) 

	case 0x70: WriteByte(RegB, AddDisplacement(RegIY, FetchByte()));	break;	// LD (IY+N),B 
	case 0x71: WriteByte(RegC, AddDisplacement(RegIY, FetchByte()));	break;	// LD (IY+N),C 
	case 0x72: WriteByte(RegD, AddDisplacement(RegIY, FetchByte()));	break;	// LD (IY+N),D 
	case 0x73: WriteByte(RegE, AddDisplacement(RegIY, FetchByte()));	break;	// LD (IY+N),E 
	case 0x74: WriteByte(RegH, AddDisplacement(RegIY, FetchByte()));	break;	// LD (IY+N),H
	case 0x75: WriteByte(RegL, AddDisplacement(RegIY, FetchByte()));	break;	// LD (IY+N),L 
	case 0x77: WriteByte(RegA, AddDisplacement(RegIY, FetchByte()));	break;	// LD (IY+N),A 

	case 0x86: AddByte(ReadByte(AddDisplacement(RegIY, FetchByte())));			break;	// ADD A,(IY+N)
	case 0x8E: AddByte(ReadByte(AddDisplacement(RegIY, FetchByte())), true);	break;	// ADC A,(IY+N) 
	case 0x96: SubByte(ReadByte(AddDisplacement(RegIY, FetchByte())));			break;	// SUB (IY+N)
	case 0x9E: SubByte(ReadByte(AddDisplacement(RegIY, FetchByte())), true);	break;	// SBC (IY+N)
	case 0xA6: AndByte(ReadByte(AddDisplacement(RegIY, FetchByte())));			break;	// AND (IY+N)
	case 0xAE: XorByte(ReadByte(AddDisplacement(RegIY, FetchByte())));			break;	// XOR (IY+N)
	case 0xB6: OrByte(ReadByte(AddDisplacement(RegIY, FetchByte())));			break;	// OR (IY+N)
	case 0xBE: CompareByte(ReadByte(AddDisplacement(RegIY, FetchByte())));		break;	// CP (IY+N)

	case 0xE1: RegIY = PopWord();						break;	// POP IY

	case 0xE3: 		
	{
		word_t data = ReadWord(RegSP);
		WriteWord(RegIY, RegSP);
		RegIY = data;
	}													break;	// EX (SP), IY

	case 0xE5: PushWord(RegIY);							break;	// PUSH IY

	case 0xE9: JumpOnCondition(ReadWord(RegIY), true);	break;	// JP (IY)

	case 0xF9: RegSP = RegIY;							break;	// LD SP, IY


#pragma region Undocumented

	case 0x26: RegIYH = FetchByte();											break; // LD HX,nn
	case 0x2E: RegIYL = FetchByte();											break; // LD LX,nn

	case 0x44: RegB = RegIYH;													break; // LD B,HY
	case 0x45: RegB = RegIYL;													break; // LD B,LY
	case 0x4C: RegC = RegIYH;													break; // LD C,HY
	case 0x4D: RegC = RegIYL;													break; // LD C,LY
	case 0x54: RegD = RegIYH;													break; // LD D,HY
	case 0x55: RegD = RegIYL;													break; // LD D,LY
	case 0x5C: RegE = RegIYH;													break; // LD E,HY
	case 0x5D: RegE = RegIYL;													break; // LD E,LY

	case 0x60: RegIYH = RegB;													break; // LD HY,B
	case 0x61: RegIYH = RegC;													break; // LD HY,C
	case 0x62: RegIYH = RegD;													break; // LD HY,D
	case 0x63: RegIYH = RegE;													break; // LD HY,E
	case 0x64: RegIYH = RegIYH;													break; // LD HY,HY
	case 0x65: RegIYH = RegIYL;													break; // LD HY,LY
	case 0x67: RegIYH = RegA;													break; // LD HY,A
		
	case 0x68: RegIYL = RegB;													break; // LD LY,B
	case 0x69: RegIYL = RegC;													break; // LD LY,C
	case 0x6A: RegIYL = RegD;													break; // LD LY,D
	case 0x6B: RegIYL = RegE;													break; // LD LY,E
	case 0x6C: RegIYL = RegIYH;													break; // LD LY,HY
	case 0x6D: RegIYL = RegIYL;													break; // LD LY,LY
	case 0x6F: RegIYL = RegA;													break; // LD LY,A

	case 0x7C: RegA = RegIYH;													break; // LD A,HY 	
	case 0x7D: RegA = RegIYL;													break; // LD A,LY 

	case 0x84: AddByte(RegIYH);													break; // ADD A, IYH
	case 0x85: AddByte(RegIYL);													break; // ADD A, IYL
	case 0x8C: AddByte(RegIYH, true);											break; // ADC A, IYH
	case 0x8D: AddByte(RegIYL, true);											break; // ADC A, IYL

	case 0x94: SubByte(RegIYH);													break; // SUB IYH
	case 0x95: SubByte(RegIYL);													break; // SUB IYL
	case 0x9C: SubByte(RegIYH, true);											break; // SBC IYH
	case 0x9D: SubByte(RegIYL, true);											break; // SBC IYL

	case 0xA4: AndByte(RegIYH);													break; 
	case 0xA5: AndByte(RegIYL);													break; 
	case 0xAC: XorByte(RegIYH);													break; 
	case 0xAD: XorByte(RegIYL);													break; 
	case 0xB4: OrByte(RegIYH);													break; 
	case 0xB5: OrByte(RegIYL);													break; 
	case 0xBC: CompareByte(RegIYH);												break; 
	case 0xBD: CompareByte(RegIYL);												break; 


	case 0x40: RegB = RegB;														break;
	case 0x41: RegB = RegC;														break;
	case 0x42: RegB = RegD;														break;
	case 0x43: RegB = RegE;														break;
	case 0x47: RegB = RegA;														break;
	case 0x48: RegC = RegB;														break;
	case 0x49: RegC = RegC;														break;
	case 0x4A: RegC = RegD;														break;
	case 0x4B: RegC = RegE;														break;
	case 0x4F: RegC = RegA;														break;

	case 0x50: RegD = RegB;														break;
	case 0x51: RegD = RegC;														break;
	case 0x52: RegD = RegD;														break;
	case 0x53: RegD = RegE;														break;
	case 0x57: RegD = RegA;														break;
	case 0x58: RegE = RegB;														break;
	case 0x59: RegE = RegC;														break;
	case 0x5A: RegE = RegD;														break;
	case 0x5B: RegE = RegE;														break;
	case 0x5F: RegE = RegA;														break;

	case 0x78: RegA = RegB;														break;
	case 0x79: RegA = RegC;														break;
	case 0x7A: RegA = RegD;														break;
	case 0x7B: RegA = RegE;														break;
	case 0x7F: RegA = RegA;														break;

#pragma endregion

	default:
		assert(false && "Unrecognized Opcode");
		break;
	}

	return m_cyclesFD[opcode];
}